﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DbGenerator.Factory.events;
using System.Data.SqlClient;
using Unity.Errors.types;
using Unity.Errors.root;

namespace DbGenerator.Factory.generator
{
    public abstract class ADbFactory
    {
        #region Main Generator
        public void GenerateFromScratch(object sender, TFactoryArgs args)
        {
            SFactoryEventManager.GenerationEvent += StartGeneration;
            SFactoryEventManager.GenerationEvent += SelectConcreteAssembly;
            SFactoryEventManager.GenerationEvent += MakeGenerationProgress;
            SFactoryEventManager.GenerationEvent += ConstructModel;
            SFactoryEventManager.GenerationEvent += MakeGenerationProgress;
            SFactoryEventManager.GenerationEvent += ConstructRelationsModel;
            SFactoryEventManager.GenerationEvent += MakeGenerationProgress;
            SFactoryEventManager.GenerationEvent += CreateDatabase;
            SFactoryEventManager.GenerationEvent += MakeGenerationProgress;
            SFactoryEventManager.GenerationEvent += CreateSchemas;
            SFactoryEventManager.GenerationEvent += MakeGenerationProgress;
            SFactoryEventManager.GenerationEvent += ConstructDbScript;
            SFactoryEventManager.GenerationEvent += MakeGenerationProgress;
            SFactoryEventManager.GenerationEvent += CommitToDatabase;
            SFactoryEventManager.GenerationEvent += GenerationProgressEnd;
            SFactoryEventManager.OnGeneration(sender, args);
        }
        public void ReadDatabaseScratch(object sender, TFactoryArgs args)
        {
            SFactoryEventManager.ReadDatabaseEvent += StartRead;
            SFactoryEventManager.ReadDatabaseEvent += SelectConcreteAssembly;
            SFactoryEventManager.ReadDatabaseEvent += MakeReadProgress;
            SFactoryEventManager.ReadDatabaseEvent += ConstructModel;
            SFactoryEventManager.ReadDatabaseEvent += MakeReadProgress;
            SFactoryEventManager.ReadDatabaseEvent += ConstructRelationsModel;
            SFactoryEventManager.ReadDatabaseEvent += ReadProgressEnd;
            SFactoryEventManager.OnReadDatabase(sender, args);
        }
        #endregion

        #region protected methods
        protected void StartGeneration(object sender, TFactoryArgs args)
        {
            args.GenerationTurtle.Start(7);
        }
        protected void StartRead(object sender, TFactoryArgs args)
        {
            args.ReadTurtle.Start(3);
        }

        protected void MakeGenerationProgress(object sender, TFactoryArgs args)
        {
            args.GenerationTurtle.MakeProgress();
        }
        protected void MakeReadProgress(object sender, TFactoryArgs args)
        {
            args.ReadTurtle.MakeProgress();
        }
        protected void GenerationProgressEnd(object sender, TFactoryArgs args)
        {
            args.GenerationTurtle.Stop();
        }
        protected void ReadProgressEnd(object sender, TFactoryArgs args)
        {
            args.ReadTurtle.Stop();
        }

        protected bool ExecuteNonQuery(SqlCommand command)
        {
            try
            {
                command.Connection.Open();
            }
            catch (Exception ex)
            {
                SErrorManager.Exception()
                    .Throw(ex, new TErrorInfo(GetType(), "ExecuteNonQuery")
                    , "Connection could not be opened.\nConnection String: {0}"
                    , command.Connection.ConnectionString);
            }
            int r = Int32.MinValue;
            try
            {
                r = command.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                SErrorManager.Exception()
                    .Throw(ex, new TErrorInfo(GetType(), "ExecuteNonQuery")
                    , "Execution of command unsuccessful. Command Text: {0}"
                    , command.CommandText);
            }
            finally
            {
                command.Connection.Close();
            }
            return r != 0;
        }
        protected int ExecuteScalar(SqlCommand command)
        {
            try
            {
                command.Connection.Open();
            }
            catch (Exception ex)
            {
                SErrorManager.Exception()
                    .Throw(ex, new TErrorInfo(GetType(), "ExecuteScalar")
                    , "Connection could not be opened.\nConnection String: {0}"
                    , command.Connection.ConnectionString);
            }
            int r = 0;
            try
            {
                r = Convert.ToInt32(command.ExecuteScalar());
            }
            catch (Exception ex)
            {
                SErrorManager.Exception()
                    .Throw(ex, new TErrorInfo(GetType(), "ExecuteScalar")
                    , "Execution of command unsuccessful. Command Text: {0}"
                    , command.CommandText);
            }
            finally
            {
                command.Connection.Close();
            }
            return r;
        }
        protected bool DatabaseExists(TFactoryArgs args)
        {
            //Generate Command:
            SqlConnection connection = args.MasterConnection;

            //Db Exists?
            string cmdText =
                String.Format("USE [master];"
                + "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'{0}') "
                + "SELECT 1 ELSE SELECT 0", args.Database.DatabaseName);
            SqlCommand command = new SqlCommand(cmdText, connection);
            bool dbExists = ExecuteScalar(command) == 1;
            return dbExists;
        }
        protected bool DropDatabase(TFactoryArgs args)
        {
            SqlConnection connection = args.MasterConnection;
            string cmdText = "USE master; "
                + "DROP DATABASE " + args.Database.DatabaseName;
            SqlCommand command = new SqlCommand(cmdText, connection);
            bool r = ExecuteNonQuery(command);
            return r;
        }
        protected bool CreateDatabase(TFactoryArgs args)
        {
            SqlConnection connection = args.MasterConnection;
            string cmdText = "USE master; "
                + "CREATE DATABASE " + args.Database.DatabaseName;
            SqlCommand command = new SqlCommand(cmdText, connection);
            bool r = ExecuteNonQuery(command);
            return r;
        }
        #endregion

        #region abstract methods
        protected abstract void SelectConcreteAssembly(object sender, TFactoryArgs args);
        protected abstract void ConstructModel(object sender, TFactoryArgs args);
        protected abstract void ConstructRelationsModel(object sender, TFactoryArgs args);
        protected abstract void CreateDatabase(object sender, TFactoryArgs args);
        protected abstract void CreateSchemas(object sender, TFactoryArgs args);
        //protected abstract void SetDbParameters(object sender, TGenerationEventArgs e);
        protected abstract void DropExistingForeignKeys(object sender, TFactoryArgs args);
        protected abstract void ConstructDbScript(object sender, TFactoryArgs args);
        //private abstract void CreateSprocs(object sender, TFactoryEventArgs args);
        //private abstract void CreateFunctions(object sender, TFactoryEventArgs args);
        //private abstract void CreteViews(object sender, TFactoryEventArgs args);
        //protected abstract void AddForeignKeyConstraints(object sender, TGenerationEventArgs args);
        protected abstract void CommitToDatabase(object sender, TFactoryArgs args);
        #endregion

    }
}
